﻿using System;
using System.Collections;
using System.Collections.Generic;
using Drill.Extensibility;
using Drill.TestSupport.TestDomains.AutomobileDomain;

namespace Drill.TestSupport.Mocks.MockDrillBits
{
	public class AutomobileDomainMockDrillBit : IDrillBit
	{
		private readonly Dictionary<Type, Func<string, object>> _getInstanceHandlerMap;
		private readonly Dictionary<Type, Func<object>> _getAllInstancesHandlerMap;

		public AutomobileDomainMockDrillBit()
		{
			ResolverObject = null;

			_getInstanceHandlerMap = new Dictionary<Type, Func<string, object>>
				{
					{ typeof(IVehicle), HandleGetInstanceIVehicle }
				};

			_getAllInstancesHandlerMap = new Dictionary<Type, Func<object>>
				{
					{ typeof(IVehicle), HandleGetAllInstancesIVehicle }
				};
		}

		private static object HandleGetInstanceIVehicle(string name)
		{
			if (string.IsNullOrWhiteSpace(name))
			{
				return new Car();
			}
			switch (name.ToLower())
			{
				case "truck":
					return new Truck();

				//case "car":
				default:
					return new Car();
			}
		}

		private static object HandleGetAllInstancesIVehicle()
		{
			return new List<IVehicle> { new Car(), new Truck() };
		}

		/// <summary>
		/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
		/// </summary>
		/// <filterpriority>2</filterpriority>
		public void Dispose()
		{
		}

		/// <summary>
		/// Gets an instance of the type specified by <paramref name="targetType"/>.
		/// </summary>
		/// <param name="targetType">The type of object to be retrieved.</param>
		/// <returns>An instance of the requested type.</returns>
		public object GetInstance(Type targetType)
		{
			Func<string, object> handler;
			return _getInstanceHandlerMap.TryGetValue(targetType, out handler) ? handler(null) : null;
		}

		/// <summary>
		/// Gets a named instance of the type specified by <paramref name="targetType"/>.
		/// </summary>
		/// <param name="targetType">The type of object to be retrieved.</param>
		/// <param name="name">The name of the specific object instance.</param>
		/// <returns>An instance of the requested named type.</returns>
		public object GetInstance(Type targetType, string name)
		{
			Func<string, object> handler;
			return _getInstanceHandlerMap.TryGetValue(targetType, out handler) ? handler(name) : null;
		}

		/// <summary>
		/// Get all instances of the type specified by <paramref name="targetType"/>.
		/// </summary>
		/// <param name="targetType">The type of object to be retrieved.</param>
		/// <returns>
		/// An <see cref="IEnumerable"/> instance representing all 
		/// available instances of the requested type.
		/// </returns>
		public IEnumerable GetAllInstances(Type targetType)
		{
			Func<object> handler;
			return _getAllInstancesHandlerMap.TryGetValue(targetType, out handler) ? (IEnumerable)handler() : null;
		}

		/// <summary>
		/// Gets an instance of the type specified by <typeparamref name="TTargetType"/>.
		/// </summary>
		/// <typeparam name="TTargetType">The type of object to be retrieved.</typeparam>
		/// <returns>An instance of the requested type.</returns>
		public TTargetType GetInstance<TTargetType>()
		{
			Func<string, object> handler;
			return _getInstanceHandlerMap.TryGetValue(typeof(TTargetType), out handler) ? (TTargetType)handler(null) : default(TTargetType);
		}

		/// <summary>
		/// Gets a named instance of the type specified by <typeparamref name="TTargetType"/>.
		/// </summary>
		/// <typeparam name="TTargetType">The type of object to be retrieved.</typeparam>
		/// <param name="name">Name the object was registered with.</param>
		/// <returns>An instance of the requested named type.</returns>
		public TTargetType GetInstance<TTargetType>(string name)
		{
			Func<string, object> handler;
			return _getInstanceHandlerMap.TryGetValue(typeof(TTargetType), out handler) ? (TTargetType)handler(name) : default(TTargetType);
		}

		/// <summary>
		/// Get all instances of the type specified by <typeparamref name="TTargetType"/>.
		/// </summary>
		/// <typeparam name="TTargetType">The type of object to be retrieved.</typeparam>
		/// <returns>
		/// An <see cref="IEnumerable{TTargetType}"/> instance representing all 
		/// available instances of the requested type.
		/// </returns>
		public IEnumerable<TTargetType> GetAllInstances<TTargetType>()
		{
			Func<object> handler;
			return _getAllInstancesHandlerMap.TryGetValue(typeof(TTargetType), out handler) ? (IEnumerable<TTargetType>)handler() : null;
		}

		/// <summary>
		/// Gets the object the DrillBit uses to resolve types.
		/// </summary>
		/// <value>
		/// The object the DrillBit uses to resolve types.
		/// </value>
		/// <remarks>
		/// The resolver object is usually the container object when the DrillBit wraps a DI container.
		/// It may also some other type of object such as a service locator object or any other type of object imaginable.
		/// </remarks>
		public object ResolverObject { get; private set; }
	}
}